package com.czk.classanlysis;

import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.util.Printer;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 查看一个方法调用了哪些方法
 *
 * @Author:ChenZhangKun
 * @Date: 2021/8/26 17:35
 */
public class MethodFindInvokeVisitor extends ClassVisitor {
    private final String methodName;
    private final String methodDesc;

    public MethodFindInvokeVisitor(int api, ClassVisitor classVisitor, String methodName, String methodDesc) {
        super(api, classVisitor);
        this.methodName = methodName;
        this.methodDesc = methodDesc;
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        if (Objects.equals(name, methodName) && Objects.equals(descriptor, methodDesc)) {
            return new MethodFindInvokeAdapter(api, null);
        }
        // 不处理，由于此时没有classWriter重新输出，所以不会漏掉该方法
        return null;
    }

    private class MethodFindInvokeAdapter extends MethodVisitor {
        private final List<String> list = new ArrayList<>();

        public MethodFindInvokeAdapter(int api, MethodVisitor methodVisitor) {
            super(api, methodVisitor);
        }

        @Override
        public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
            // 首先，处理自己的代码逻辑
            String info = String.format("%s %s.%s%s", Printer.OPCODES[opcode], owner, name, descriptor);
            if (!list.contains(info)) {
                list.add(info);
            }
            super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
        }

        @Override
        public void visitEnd() {
            for (String s : list) {
                System.out.println(s);
            }
            // 调用父类方法
            super.visitEnd();
        }
    }
}
